package elka.sag.logisticsystem.actor;

import org.apache.log4j.Logger;

import elka.sag.logisticsystem.bean.ActionStatus;
import elka.sag.logisticsystem.bean.Order;
import elka.sag.logisticsystem.message.ProcessMessage;
import elka.sag.logisticsystem.message.SimpleMessage;
import elka.sag.logisticsystem.service.RepositoryService;
import elka.sag.logisticsystem.service.RuleService;
import akka.actor.Actor;
import akka.actor.ActorRef;
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.actor.UntypedActorFactory;
import akka.routing.RoundRobinRouter;

/**
 * Klasa bazowa aktora
 * 
 * @author Wojtek
 * 
 */
public abstract class BaseActor extends UntypedActor {
	private static Logger LOGGER = Logger.getLogger(BaseActor.class);
	
	protected RepositoryService repositoryService;
	protected RuleService ruleService;

	@Override
	public final void onReceive(Object message) throws Exception {
		//dla symulacji przetwarzania czekamy przez losowy czas 0-10s
		int waitForTime = (int) (Math.random() * 800);
		Thread.sleep(waitForTime);
		
		// wszystkie komunikaty sa typu SimpleMessage, ProcessMessage lub
		// pochodne
		if (message instanceof SimpleMessage) {
			SimpleMessage msg = (SimpleMessage) message;
			processMessage(msg);
		} else if (message instanceof ProcessMessage) {
			ProcessMessage msg = (ProcessMessage) message;
			processMessage(msg);
		} else {
			unhandled(message);
		}
	}

	/**
	 * Wlasciwe przetworzenie komunikatu
	 * 
	 * @param message
	 * @return
	 */
	abstract protected void processMessage(SimpleMessage message)
			throws Exception;

	/**
	 * Przetwarzanie komunikatu rozpoczecia przetwarzania Komunikat obslugiwany
	 * tylko przez ControlAgent - dlatego metoda nie jest abstrakcyjna
	 * 
	 * @param message
	 * @throws Exception
	 */
	protected void processMessage(ProcessMessage message) throws Exception {
		unhandled(message);
	}

	/**
	 * Tworzy aktora i wstrzykuje referencje do repositoryService
	 * 
	 * @param actorClass
	 * @return
	 */
	protected ActorRef createActor(final String actorClass,
			final int numberOfActors) {
		@SuppressWarnings("serial")
		Props props = new Props(new UntypedActorFactory() {

			public Actor create() {
				BaseActor actor = null;
				try {
					actor = (BaseActor) Class.forName(actorClass).newInstance();
					actor.setRepositoryService(repositoryService);
					actor.setRuleService(ruleService);
				} catch (InstantiationException e) {
					LOGGER.warn("Blad tworzenie agenta " + actorClass, e);
				} catch (IllegalAccessException e) {
					LOGGER.warn("Blad tworzenie agenta " + actorClass, e);
				} catch (ClassNotFoundException e) {
					LOGGER.warn("Blad tworzenie agenta " + actorClass, e);
				}

				return actor;
			}
		});

		if (numberOfActors > 1) {
			return this.getContext().actorOf(
					props.withRouter(new RoundRobinRouter(numberOfActors)));
		} else {
			return this.getContext().actorOf(props);
		}

	}

	/**
	 * Wyznacza kolejny status zamowienia
	 * Jezeli zostalo poprawnie przeprocesowane to nastepny status jest pobierany z konfiguracji.
	 * W przeciwnym przypadku wyznaczany jest na podstawie regul.
	 * @param order
	 * @param actionStatus
	 * @return
	 */
	protected Order.OrderStatus getNextOrderStatus(Order order, ActionStatus actionStatus) {
		if(ActionStatus.OK.equals(actionStatus)) {
			return repositoryService.getNextStatus(order.getStatus());
		} else {
			return ruleService.getNextStatus(order, actionStatus, getClass().getName());
		}
	}
	
	public RepositoryService getRepositoryService() {
		return repositoryService;
	}

	public void setRepositoryService(RepositoryService repositoryService) {
		this.repositoryService = repositoryService;
	}

	public RuleService getRuleService() {
		return ruleService;
	}

	public void setRuleService(RuleService ruleService) {
		this.ruleService = ruleService;
	}

}
